package com.example.lottery_system.service.impl;

import com.example.lottery_system.common.errcode.ServiceErrorCodeConstant;
import com.example.lottery_system.common.exception.ServiceException;
import com.example.lottery_system.common.utils.JacksonUtil;
import com.example.lottery_system.common.utils.RedisUtil;
import com.example.lottery_system.controller.param.*;
import com.example.lottery_system.controller.result.PageResult;
import com.example.lottery_system.dao.DataObject.ActivityDO;
import com.example.lottery_system.dao.DataObject.ActivityPrizeDO;
import com.example.lottery_system.dao.DataObject.ActivityUserDO;
import com.example.lottery_system.dao.DataObject.PrizeDO;
import com.example.lottery_system.dao.Mapper.*;
import com.example.lottery_system.service.ActivityService;
import com.example.lottery_system.service.DrawPrizeService;
import com.example.lottery_system.service.Enum.ActivityPrizeStatusEnum;
import com.example.lottery_system.service.Enum.ActivityPrizeTiersEnum;
import com.example.lottery_system.service.Enum.ActivityStatusEnum;
import com.example.lottery_system.service.Enum.ActivityUserStatusEnum;
import com.example.lottery_system.service.dto.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ActivityServiceImpl implements ActivityService {

    @Autowired
    PrizeMapper prizeMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    ActivityMapper activityMapper;

    @Resource
    ActivityUserMapper activityUserMapper;

    @Resource
    ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    DrawPrizeService drawPrizeService;

    @Resource
    RedisUtil redisUtil;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WinningRecordMapper winningRecordMapper;
    private static final  String ACTIVITY_NAME_PREV = "ACTIVITY_INFO_";

    private static final long ACTIVITY_OUT_TIME = 3 * 24 * 60 * 60;



    @Override
    @Transactional(rollbackFor = Exception.class)
    public ActivityDto createActivity(ActivityParam param) {
        /**
         * 1. 校验活动信息
         *
         */
        checkActivityParam(param);
        /**
         * 2. 创建活动并保存
         *
         */
        ActivityDO activityDo = new ActivityDO();
        activityDo.setActivityName(param.getActivityName());
        activityDo.setDescription(param.getDescription());
        activityDo.setStatus("running");
        activityMapper.insert(activityDo);

        /**
         * 3.关联活动人员并保存
         *
         */
        List<ActivityUserParam> activityUserParamList = param.getActivityUserList();
        List<ActivityUserDO> activityUserDoList = activityUserParamList.stream().map(
                activityUserParam -> new ActivityUserDO(
                        activityDo.getId(),
                        activityUserParam.getUserId(),
                        activityUserParam.getUserName(),
                        "running")
        ).collect(Collectors.toList());
        activityUserMapper.insert(activityUserDoList);

        /**
         *
         * 4. 关联奖品信息并保存
         */
        List<ActivityPrizeParam> activityPrizeParamList = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDoList = activityPrizeParamList.stream().map(
                activityPrizeParam -> new ActivityPrizeDO(
                        activityDo.getId(),
                        activityPrizeParam.getPrizeId(),
                        activityPrizeParam.getPrizeAmount(),
                        activityPrizeParam.getPrizeTiers(),
                        "running"
                )
        ).collect(Collectors.toList());
        activityPrizeMapper.insert(activityPrizeDoList);

        /**
         * 5. 整合活动 + 奖品 +  人员完整信息并保存在 redis 中
         */
//        首先先获取到 奖品id

        List<Long> prizeIds = activityPrizeParamList.stream().
                map(ActivityPrizeParam::getPrizeId).distinct().collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.selectByPrizeId(prizeIds);
        ActivityDetailDTO activityDetailDTO = getActivityDetailDTO(activityDo.getId());
        conformRedis(activityDetailDTO);

        /**
         * 6. 整合返回数据
         */

        ActivityDto activityDto = new ActivityDto();
        activityDto.setId(activityDo.getId());

        return activityDto;
    }

    /**
     *
     * 翻页查询活动列表信息
     *
     * @param param
     * @return
     */
    @Override
    public PageResult<List<ActivityListDto>> findActivityList(PageParam param) {
        // 查询总结果
        int count = activityMapper.count();


        // 查询结果
        List<ActivityDO> activityDoList = activityMapper.selectAllByPage(
                param.offset(param.getCurrentPage(),param.getPageSize())
                ,param.getPageSize());

        // 转化格式返回
        List<ActivityListDto> activityListDtoList = activityDoList.stream().map(
                activityDo -> new ActivityListDto(
                        activityDo.getId(),
                        activityDo.getActivityName(),
                        activityDo.getDescription(),
                        ActivityStatusEnum.forName(activityDo.getStatus())
                )
        ).collect(Collectors.toList());

//        返回数据
        PageResult<List<ActivityListDto>> pageResult = new PageResult<>();
        pageResult.setTotal(count);
        pageResult.setRecords(activityListDtoList);

        return pageResult;
    }

    /**
     * 保存活动详情信息
     * @param
     */

    @Override
    public void cacheActivityToRedis(ActivityDetailDTO activityDetailDTO) {

//        保存到Redis中
        conformRedis(activityDetailDTO);
    }


    /**
     *
     * 先从 Redis里面查询
     * 如果没有查询到从数据库中去查询
     *
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO findActivityDetailFromRedis(Long activityId) {
        // 先查缓存
        ActivityDetailDTO activityDetailDTO =  getActDetailByActIdFromRedis(activityId);
        if (null != activityDetailDTO) {
            return activityDetailDTO;
        }

        // 如果缓存中不存在就查询数据库
        activityDetailDTO =  getActivityDetailDTO(activityId);
        cacheActivityToRedis(activityDetailDTO);

//        重合整合数据返回
        return activityDetailDTO;
    }

    @Override
    public Boolean deleteActivity(Long activityId) {
//        检验
        if (null == activityId) {
            throw  new ServiceException(ServiceErrorCodeConstant.ACTIVITY_PARAM_IS_EMPTY);
        }
        try {
            //先删缓存中的数据
            deleteActivityToRedis(activityId);
            //最后删除库表数据
            // 删除活动列表
             activityMapper.deleteActivity(activityId);

//             删除活动用户相关信息
             activityUserMapper.deleteByActivityId(activityId);
//             删除活动奖品相关信息
             activityPrizeMapper.deleteByActivityId(activityId);
//            删除中奖记录
            winningRecordMapper.deleteDOByAP(activityId,null);


            return true;
        } catch (Exception e) {
            throw new ServiceException(ServiceErrorCodeConstant.ACTIVITY_DELETE_ERROR);
        }

    }

    private void deleteActivityToRedis(Long activityId) {
//        校验
        if (null == activityId) {
            log.warn("需要删除的deleteActivityToRedis的key为空!");
            return;
        }


        // 先删除中奖信息：活动维度和活动奖品维度
        drawPrizeService.deleteWinningReCord(activityId);


//        最后删除活动信息
        String str = ACTIVITY_NAME_PREV + activityId;
        if (redisUtil.contains(str)) {
//            存在的时候就进行删除
            redisUtil.delete(str);
        }
    }

    /**
     *
     * 从缓存中获取活动详情信息
     *
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActDetailByActIdFromRedis(Long activityId) {
        if (null == activityId) {
            return null;
        }

        String key = ACTIVITY_NAME_PREV + activityId;
        // 判断是否存在key
        if (!redisUtil.contains(key)) {
            return null;
        }

        // 进行查询
        String s = redisUtil.get(key);
        ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(s, ActivityDetailDTO.class);
        return activityDetailDTO;
    }

    /**
     * 整合活动详情信息
     */
    public ActivityDetailDTO getActivityDetailDTO(Long activityId) {

        // 查询活动
        if (null == activityId) {
            log.warn("cacheActivity::activityId为空!");
            return new ActivityDetailDTO();
        }

        // 查询活动
        ActivityDO activityDo = activityMapper.selectByActivityId(activityId);
        if (null == activityDo) {
            log.warn("cacheActivity::activityDo为空!");
            throw new ServiceException(ServiceErrorCodeConstant.CACHE_ACTIVITY_IS_EMPTY);
        }

        List<ActivityUserDO>  activityUserDoList = activityUserMapper.selectByActivityId(activityId);
        if (CollectionUtils.isEmpty(activityUserDoList)) {
            log.warn("cacheActivity::activityUserDoList为空!");
            throw  new ServiceException(ServiceErrorCodeConstant.CACHE_ACTIVITY_ERROR);
        }

        List<ActivityPrizeDO> activityPrizeDoList = activityPrizeMapper.selectByActivityId(activityId);
        if (CollectionUtils.isEmpty(activityPrizeDoList)) {
            log.warn("cacheActivity::activityPrizeDoList为空!");
            throw  new ServiceException(ServiceErrorCodeConstant.CACHE_ACTIVITY_ERROR);
        }
        List<PrizeDO> prizeDOList = prizeMapper.selectByPrizeId(
                activityPrizeDoList.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList())
        );
        if (CollectionUtils.isEmpty(prizeDOList)) {
            log.warn("cacheActivity::prizeDoList为空!");
            throw  new ServiceException(ServiceErrorCodeConstant.CACHE_ACTIVITY_ERROR);
        }

        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        // 先设置活动用户列表
        List<UserDto> userDtoList = activityUserDoList.stream().map(aud -> new UserDto(
                        aud.getUserId(),
                        aud.getUserName(),
                        ActivityUserStatusEnum.forName(aud.getStatus())
                ))
                .collect(Collectors.toList());

        // 设置活动奖品列表
        List<PrizeDto> prizeDtoList = activityPrizeDoList.stream().map(apd -> {
                    PrizeDto prizeDto = new PrizeDto();
//                    获取 奖品信息
                    for (PrizeDO prizeDo : prizeDOList) {
                        if (apd.getPrizeId().equals(prizeDo.getId())) {
                            prizeDto.setId(prizeDo.getId());
                            prizeDto.setName(prizeDo.getName());
                            prizeDto.setDesc(prizeDo.getDescription());
                            prizeDto.setAmount(apd.getPrizeAmount());
                            prizeDto.setPrizeTiers(ActivityPrizeTiersEnum.forName(apd.getPrizeTiers()));
                            prizeDto.setImageUrl(prizeDo.getImageUrl());
                            prizeDto.setStatus(ActivityPrizeStatusEnum.forName(apd.getStatus()));
                            prizeDto.setPrice(prizeDo.getPrice());
                            break;
                        }
                    }

                    return prizeDto;
                })
                .collect(Collectors.toList());


//        整合数据
        activityDetailDTO.setActivityId(activityDo.getId());
        activityDetailDTO.setActivityName(activityDo.getActivityName());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));
        activityDetailDTO.setDescription(activityDo.getDescription());
        activityDetailDTO.setUserDtoList(userDtoList);
        activityDetailDTO.setPrizeDtoList(prizeDtoList);

        return activityDetailDTO;
    }

    public void  conformRedis(ActivityDetailDTO activityDetailDTO) {

        //        存放在 Redis 中
        String key = ACTIVITY_NAME_PREV + activityDetailDTO.getActivityId();

        try {
            redisUtil.put(key, JacksonUtil.writeValueAsString(activityDetailDTO),ACTIVITY_OUT_TIME);
        } catch (Exception e) {
            log.warn("活动详情信息插入失败! key = {}, value = {}  错误信息：{}",key, JacksonUtil.writeValueAsString(activityDetailDTO)
                    ,e.getMessage());
        }

    }

    private void checkActivityParam(ActivityParam param) {
        // 检查活动名不能为空
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstant.ACTIVITY_PARAM_IS_EMPTY);
        }

        // 查找该奖品是否存在
        List<Long> prizeIdList = new ArrayList<>();
        for (ActivityPrizeParam activityPrizeParam: param.getActivityPrizeList()) {
             prizeIdList.add(activityPrizeParam.getPrizeId());
        }
//        去重
        prizeIdList =  prizeIdList.stream().distinct().collect(Collectors.toList());
//        检查活动奖品是否存在
        List<Long> idLists =  prizeMapper.selectIdByPrizeId(prizeIdList);
        for (Long id : prizeIdList) {
            if (!idLists.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstant.ACTIVITY_PRIZE_NOT_EXIST);
            }
        }

//        检查活动用户是否存在
        List<Long> userIdList = new ArrayList<>();
        for (ActivityUserParam userParam: param.getActivityUserList()) {
            userIdList.add(userParam.getUserId());
        }

        List<Long> activityUserIdList = userMapper.selectIdByUserId(userIdList);
        for (Long id: userIdList) {
            if (!activityUserIdList.contains(id)) {
                throw  new ServiceException(ServiceErrorCodeConstant.ACTIVITY_USER_NOT_EXIST);
            }
        }

        // 检查活动奖品数量是否 > 活动用户数量
        int userCount = userIdList.size();
        long prizeCount =  param.getActivityPrizeList().stream().mapToLong(ActivityPrizeParam::getPrizeAmount).sum();
        if (prizeCount > userCount) {
            throw new ServiceException(ServiceErrorCodeConstant.ACTIVITY_PRIZE_USER_ERROR);
        }



    }
}
